﻿namespace dogd
{
	using System;
	using System.Data.Common;
	using System.Data.SQLite;
	using System.Reflection;

	/// <summary>
	/// ParameterBuild creates parameters for database reads or writes using TableMetadata.
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class ParameterBuilder<T> where T : new ()
	{
		/// <summary>
		/// Initialize with a parameter factory.
		/// </summary>
		/// <param name="factory"></param>
		public ParameterBuilder(IParameterFactory factory)
		{
			this.Factory = factory;
		}

		private IParameterFactory Factory { get; set; }

		/// <summary>
		/// Create parameters for each property in the object instance that can be written to 
		/// the database.
		/// </summary>
		/// <param name="instance"></param>
		/// <param name="metadata"></param>
		/// <param name="option"></param>
		/// <param name="command"></param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "As intended")]
		public void ConstructForWrite(T instance, TableMetadata metadata, ParameterOption option, DbCommand command)
		{
			if (command == null)
			{
				throw new ArgumentNullException("command", "Cannot be null");
			}

			Type instanceType = typeof(T);

			foreach (var column in metadata.Columns)
			{
				if (!column.IsPrimaryKey || option == ParameterOption.IncludePrimaryKey)
				{
					var pi = instanceType.GetProperty(column.PropertyName);
					var value = pi.GetValue(instance, null);
					var parameter = this.Factory.CreateWriteParameter(value, column);
					command.Parameters.Add(parameter);
				}
			}
		}

		/// <summary>
		/// Creates a parameter based on the key value. 
		/// </summary>
		/// <param name="lookup"></param>
		/// <param name="metadata"></param>
		/// <param name="command"></param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "As intended")]
		public void ConstructForRead(Key lookup, ColumnMetadata metadata, DbCommand command)
		{
			if (lookup == null || !lookup.IsValid)
			{
				throw new ArgumentNullException("lookup", "Cannot be null");
			}

			if (metadata == null)
			{
				throw new ArgumentNullException("metadata", "Cannot be null");
			}

			if (command == null)
			{
				throw new ArgumentNullException("command", "Cannot be null");
			}

			var parameter = this.Factory.CreateReadParameter(lookup, metadata);
			command.Parameters.Add(parameter);
		}

		/// <summary>
		/// Creates a read parameter based on the string lookup.
		/// </summary>
		/// <param name="lookup"></param>
		/// <param name="metadata"></param>
		/// <param name="command"></param>
		public void ConstructForRead(string lookup, ColumnMetadata metadata, DbCommand command)
		{
			if (string.IsNullOrEmpty(lookup))
			{
				throw new ArgumentNullException("lookup", "Cannot be blank");
			}

			if (metadata == null)
			{
				throw new ArgumentNullException("metadata", "Cannot be null");
			}

			if (command == null)
			{
				throw new ArgumentNullException("command", "Cannot be null");
			}

			var parameter = this.Factory.CreateReadParameter(lookup, metadata);
			command.Parameters.Add(parameter);
		}
	}
}
